#ifndef _META_FUNCTOR_INC_
#define _META_FUNCTOR_INC_

#include "Meta.Typelist.h"
#include "Meta.EmptyType.h"
#include "Meta.FunctorImpl.h"

#include <typeinfo>
#include <memory>

namespace Maha
{
      namespace Meta
      {
            template <typename ResultType = void, class TList = NullType>
            class Functor
            {
            public:
                  typedef FunctorImpl<ResultType, TList> Impl;
                  typedef typename Impl::Parm1 Parm1;
                  typedef typename Impl::Parm2 Parm2;
                  typedef typename Impl::Parm3 Parm3;
                  typedef typename Impl::Parm4 Parm4;

                  Functor() : impl(0) {}
                    
                  Functor(const Functor& rhs)
		            : impl(Impl::Clone(rhs.impl.get())) {}
                    
                  Functor(std::auto_ptr<Impl> impl)
		            : impl(impl) {}
                    
                  template <typename Fun>
                  Functor(Fun fun) 
		            : impl(new FunctorHandler<Functor, Fun>(fun)) {}

                  template <class PtrObj, typename MemFn>
                  Functor(const PtrObj& p, MemFn memFn)
                        : impl(new MemFunHandler<Functor, PtrObj, MemFn>(p, memFn)) {}

                  typedef Impl * (std::auto_ptr<Impl>::*unspecified_bool_type)() const;

                  operator unspecified_bool_type() const
                  {
                        return impl.get() ? &std::auto_ptr<Impl>::get : 0;
                  }

                  Functor& operator=(const Functor& rhs)
                  {
                        swap_auto_ptrs(rhs);
                        return *this;
                  }

                  ResultType operator()() const
                  {
                        //LOKI_FUNCTION_THROW_BAD_FUNCTION_CALL
                        return (*impl)(); 
                  }

                  ResultType operator()(Parm1 p1) const
                  { 
                        //LOKI_FUNCTION_THROW_BAD_FUNCTION_CALL
                        return (*impl)(p1); 
                  }
                    
                  ResultType operator()(Parm1 p1, Parm2 p2) const
                  {    
                        //LOKI_FUNCTION_THROW_BAD_FUNCTION_CALL
                        return (*impl)(p1, p2); 
                  }
                    
                  ResultType operator()(Parm1 p1, Parm2 p2, Parm3 p3) const
                  {    
                        //LOKI_FUNCTION_THROW_BAD_FUNCTION_CALL
                        return (*impl)(p1, p2, p3); 
                  }
                    
                  ResultType operator()(Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4) const
                  { 
                        //LOKI_FUNCTION_THROW_BAD_FUNCTION_CALL
                        return (*impl)(p1, p2, p3, p4); 
                  }

            private:
            
                  inline void swap_auto_ptrs(const Functor& fctor)
                  {
                        Functor copy(fctor);
			      Impl* p = impl.release();
                        impl.reset(copy.impl.release());
                        copy.impl.reset(p);
		      }
                  
                  std::auto_ptr<Impl> impl;
      };
    
      } // Meta
} // Maha


#endif // _META_FUNCTOR_INC_

